// Copyright (c) 2009 - Quildreen <quildreen@gmail.com>                     
//                                                                          
//  Permission is hereby granted, free of charge, to any person              
//  obtaining a copy of this software and associated documentation files     
//  (the "Software"), to deal in the Software without restriction,           
//  including without limitation the rights to use, copy, modify, merge,     
//  publish, distribute, sublicense, and/or sell copies of the Software,     
//  and to permit persons to whom the Software is furnished to do so,        
//  subject to the following conditions:                                     
//                                                                           
//  The above copyright notice and this permission notice shall be           
//  included in all copies or substantial portions of the Software.          
//                                                                           
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,          
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF       
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                    
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE   
//  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION   
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION    
//  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.          

/**
 * lily.time
 *
 * Implements timed events that runs within a maximum given FPS.
 **/
lily.defineModule("time");


/**
 * class lily.time.Clock
 *
 * Helps to track time and limit frame rate.
 **/
lily.time.Clock = Class.create(
{
    /**
     * new lily.time.Clock([on_tick[, max_fps=40[, auto_start=true]]])
     * - on_tick (Function): optional callback for each clock tick.
     * - max_fps (Number):   maximum FPS for this clock.
     * - auto_start (Boolean): Whether to start clock after instanciating.
     **/
    initialize: function(on_tick, max_fps, auto_start)
    {
        this._frame_count = 0;
        this._frame_time = 0;

        /**
         * lily.time.Clock#_updater -> Number
         * 
         * A numeric ID for the setTimeout function.
         **/
        this._updater = null;

        /**
         * lily.time.Clock#max_fps -> Number
         * 
         * the max number of frames per second this clock should run at.
         **/
        this.max_fps = max_fps || 40;

        /**
         * lily.time.Clock#interval -> Number (read only)
         * 
         * The interval between each clock tick, in miliseconds.
         * 
         * This is just a cached property based on the
         * [[lily.time.Clock.max_fps]] attribute, thus supposed to be read
         * only.
         **/
        this._interval = 1000/this.max_fps;

        /**
         * lily.time.Clock#fps -> Number (read only)
         * 
         * Holds the current framerate the game's running at.
         * 
         * This is calculated at each clock tick, thus should be read only.
         **/
        this.fps = 0;

        /**
         * lily.time.Clock#onTick -> Function
         * 
         * A callback called at each clock tick, with the following signature:
         * 
         *     function(Number lag)
         *     
         * 
         * The single expected parameter is the amount of unexpected delay
         * between the last frame and the current one, in miliseconds.
         **/
        this.onTick = on_tick;

        this.__tick = this._tick.bind(this);
        this.paused = !auto_start;

        if (auto_start) {
            this.start();
        }
    },


    /**
     * lily.time.Clock#_tick() -> undefined
     * 
     * Called at each clock tick to update data and fire the
     * [[lily.time.Clock.onTick]] callback.
     **/
    _tick: function()
    {
        /* calculates lag */
        var st = +new Date();
        var lag = st - (this._old_st || (this._old_st = st));
        this._old_st = st;

        /* calculates the frame rate this program is running */
        this._frame_count++;
        this._frame_time += lag;
        if (this._frame_time >= 1000) {
            this.fps = this._frame_count;
            this._frame_count = 0;
            this._frame_time = this._frame_time % 1000;
        }

        lag = lag - this._interval;
        if (lag <= 0) {
            lag = 1;
        }

        this.onTick && this.onTick(lag);

        if (!this.paused) {
            this.start();
        }
    },


    /**
     * lily.time.Clock#stop() -> undefined
     * 
     * Stops the clock.
     **/
    stop: function()
    {
        this.paused = true;
        clearTimeout(this._updater);
    },


    /**
     * lily.time.Clock#start() -> undefined
     **/
    start: function()
    {
        this.paused = false;
        this._updater = setTimeout(this.__tick, this._interval);
    },


    /**
     * lily.time.Clock#changeFPS([max_fps = 40]) -> undefined
     * - max_fps (Number): the maximum number of frames per second.
     * 
     * Changes the FPS the clock should run at.
     **/
    changeFPS: function(max_fps)
    {
        this.max_fps = max_fps || 40;
        this._interval = 1000 / this.max_fps;
        this.stop();
        this.start();
    }
});